/***********************************************************
 *  File_Name  : XJBufferPool.cpp
 *  File_Path   :
 *  File_Encode : UTF8
 *  Description : 简单内存缓冲池
 *
 *  OS : Linux, UNIX, Windows
 *  LastVersion  : 20150918
 *
 *  Author      : LiXiujie(李秀杰)
 *  Date        : 2015-9-18
 *  Version     : 20150918
 *  Description :
 *
 *  Edit Author :
 *  Edit Date   :
 *  Version     :
 *  Description :
 *
 ***********************************************************/

#include "XJBufferPool.h"
#include "XJSysHelper.h"

#ifndef _WIN32
#include <algorithm>
#endif

namespace XJUtilClass{

const unsigned long TItem::DEFAULT_ITEM_CAPACITY			= SysGetPageSize();
const unsigned long XJItemPool::DEFAULT_ITEM_CAPACITY		= TItem::DEFAULT_ITEM_CAPACITY;
const unsigned long XJItemPool::DEFAULT_POOL_SIZE			= 300;
const unsigned long XJItemPool::DEFAULT_POOL_HOLD			= 900;
const unsigned long XJBufferPool::DEFAULT_ITEM_CAPACITY		= XJItemPool::DEFAULT_ITEM_CAPACITY;
const unsigned long XJBufferPool::DEFAULT_ITEM_POOL_SIZE		= XJItemPool::DEFAULT_POOL_SIZE;
const unsigned long XJBufferPool::DEFAULT_ITEM_POOL_HOLD		= XJItemPool::DEFAULT_POOL_HOLD;
const unsigned long XJBufferPool::DEFAULT_BUFFER_LOCK_TIME	= 5000;
const unsigned long XJBufferPool::DEFAULT_BUFFER_POOL_SIZE	= 150;
const unsigned long XJBufferPool::DEFAULT_BUFFER_POOL_HOLD	= 450;

TItem* TItem::Construct(XJPrivateHeap& heap, int capacity, unsigned char* pData, int length)
{
	ASSERT(capacity > 0);

	int item_size	= sizeof(TItem);
	TItem* pItem	= (TItem*)heap.Alloc(item_size + capacity);
	pItem->head		= (unsigned char*)pItem + item_size;

	//pItem->TItem::TItem(heap, capacity, pData, length);
	pItem = new (pItem) TItem(heap, capacity, pData, length);
	return pItem;
}

void TItem::Destruct(TItem* pItem)
{
	ASSERT(pItem != NULL);

	XJPrivateHeap& heap = pItem->heap;
	pItem->TItem::~TItem();
	//delete (pItem);
	heap.Free(pItem);
}

inline int TItem::Cat(const unsigned char* pData, int length)
{
	ASSERT(pData != NULL && length > 0);

	int cat = std::min(Remain(), length);

	if(cat > 0)
	{
		memcpy(end, pData, cat);
		end += cat;
	}

	return cat;
}

inline int TItem::Cat(const TItem& other)
{
	ASSERT(this != &other);
	return Cat(other.Ptr(), other.Size());
}

inline int TItem::Fetch(unsigned char* pData, int length)
{
	ASSERT(pData != NULL && length > 0);

	int fetch = std::min(Size(), length);
	memcpy(pData, begin, fetch);
	begin	 += fetch;

	return fetch;
}

inline int TItem::Reduce(int length)
{
	ASSERT(length > 0);

	int reduce   = std::min(Size(), length);
	begin		+= reduce;

	return reduce;
}

inline void	TItem::Reset(int first, int last)
{
	ASSERT(first >= -1 && first <= capacity);
	ASSERT(last >= -1 && last <= capacity);

	if(first >= 0)	begin	= head + std::min(first, capacity);
	if(last >= 0)	end		= head + std::min(last, capacity);
}

int TItemList::Cat(const unsigned char* pData, int length)
{
	int remain = length;

	while(remain > 0)
	{
		TItem* pItem = Back();

		if(pItem == NULL || pItem->IsFull())
			pItem = PushBack(itPool.PickFreeItem());

		int cat  = pItem->Cat(pData, remain);

		pData	+= cat;
		remain	-= cat;
	}

	return length;
}

int TItemList::Cat(const TItem* pItem)
{
	return Cat(pItem->Ptr(), pItem->Size());
}

int TItemList::Cat(const TItemList& other)
{
	ASSERT(this != &other);

	int length = 0;

	for(TItem* pItem = other.Front(); pItem != NULL; pItem = pItem->next)
		length += Cat(pItem);

	return length;
}

int TItemList::Fetch(unsigned char* pData, int length)
{
	int remain = length;

	while(remain > 0 && Size() > 0)
	{
		TItem* pItem = Front();
		int fetch	 = pItem->Fetch(pData, remain);

		pData	+= fetch;
		remain	-= fetch;

		if(pItem->IsEmpty())
			itPool.PutFreeItem(PopFront());
	}

	return length - remain;
}

int TItemList::Reduce(int length)
{
	int remain = length;

	while(remain > 0 && Size() > 0)
	{
		TItem* pItem = Front();
		remain		-= pItem->Reduce(remain);

		if(pItem->IsEmpty())
			itPool.PutFreeItem(PopFront());
	}

	return length - remain;
}

void TItemList::Release()
{
	itPool.PutFreeItem(*this);
}

void XJItemPool::PutFreeItem(TItem* pItem)
{
	ASSERT(pItem != NULL);

	unsigned long size = m_lsFreeItem.Size();

	if(size < m_dwPoolHold)
	{
		XJMutexLocalLock locallock(m_csFreeItem);
		m_lsFreeItem.PushBack(pItem);
	}
	else
	{
		TItem::Destruct(pItem);
		CompressFreeItem(m_dwPoolSize);
	}
}

void XJItemPool::PutFreeItem(TItemList& lsItem)
{
	unsigned long addSize = lsItem.Size();

	if(addSize > 0)
	{
		unsigned long cacheSize = m_lsFreeItem.Size();
		unsigned long totalSize = addSize + cacheSize;

		if(totalSize <= m_dwPoolHold)
		{
			XJMutexLocalLock locallock(m_csFreeItem);
			m_lsFreeItem.Shift(lsItem);
		}
		else
		{
			lsItem.Clear();

			if(cacheSize >= m_dwPoolHold)
				CompressFreeItem(m_dwPoolSize);
		}
	}
}

TItem* XJItemPool::PickFreeItem()
{
	TItem* pItem = NULL;

	if(m_lsFreeItem.Size() > 0)
	{
		XJMutexLocalLock locallock(m_csFreeItem);

		if(m_lsFreeItem.Size() > 0)
			pItem = m_lsFreeItem.PopFront();
	}

	if(pItem == NULL)
		pItem = TItem::Construct(m_heap, m_dwItemCapacity);
	else
		pItem->Reset();

	return pItem;
}

inline void XJItemPool::Clear()
{
	{
		XJMutexLocalLock locallock(m_csFreeItem);
		m_lsFreeItem.Clear();
	}

	m_heap.Reset();
}

void XJItemPool::CompressFreeItem(int size)
{
	XJMutexLocalLock locallock(m_csFreeItem);

	while(m_lsFreeItem.Size() > size)
		TItem::Destruct(m_lsFreeItem.PopFront());
}

TBuffer* TBuffer::Construct(XJBufferPool& pool, unsigned long int dwID)
{
	ASSERT(dwID != 0);

	XJPrivateHeap& heap	= pool.GetPrivateHeap();
	TBuffer* pBuffer	= (TBuffer*)heap.Alloc(sizeof(TBuffer));

	//pBuffer->TBuffer::TBuffer(heap, pool.GetItemPool(), dwID);
	new (pBuffer) TBuffer(heap, pool.GetItemPool(), dwID);
	return pBuffer;
}

void TBuffer::Destruct(TBuffer* pBuffer)
{
	ASSERT(pBuffer != NULL);

	XJPrivateHeap& heap = pBuffer->heap;
	pBuffer->TBuffer::~TBuffer();
	heap.Free(pBuffer);
}

inline void TBuffer::Reset()
{
	id		 = 0;
	length	 = 0;
	freeTime = TimeGetTime();
}

int TBuffer::Cat(const unsigned char* pData, int len)
{
	items.Cat(pData, len);
	return IncreaseLength(len);
}

int TBuffer::Cat(const TItem* pItem)
{
	items.Cat(pItem);
	return IncreaseLength(pItem->Size());
}

int TBuffer::Cat(const TItemList& other)
{
	ASSERT(&items != &other);

	for(TItem* pItem = other.Front(); pItem != NULL; pItem = pItem->next)
		Cat(pItem);

	return length;
}

int TBuffer::Fetch(unsigned char* pData, int len)
{
	int fetch = items.Fetch(pData, len);
	DecreaseLength(fetch);

	return fetch;
}

int TBuffer::Reduce(int len)
{
	int reduce = items.Reduce(len);
	DecreaseLength(reduce);

	return reduce;
}

void XJBufferPool::PutFreeBuffer(unsigned long dwID)
{
	ASSERT(dwID != 0);

	TBuffer* pBuffer = FindCacheBuffer(dwID);

	if(pBuffer != NULL)
	{
		{
			XJRWWriteLock locallock(m_csBufferMap);
			m_mpBuffer.erase(dwID);
		}

		PutFreeBuffer(pBuffer);
	}
}

void XJBufferPool::PutFreeBuffer(TBuffer* pBuffer)
{
	if(pBuffer->IsValid())
	{
		bool bOK = false;

		{
			XJMutexLocalLock locallock(pBuffer->cs);

			if(pBuffer->IsValid())
			{
				pBuffer->Reset();
				bOK = true;
			}
		}

		if(bOK)
		{
			m_itPool.PutFreeItem(pBuffer->items);

			{
				XJMutexLocalLock locallock(m_csFreeBuffer);
				m_lsFreeBuffer.PushBack(pBuffer);
			}

			if((unsigned long)m_lsFreeBuffer.Size() > m_dwBufferPoolHold)
				CompressFreeBuffer(m_dwBufferPoolSize);
		}
	}
}

void XJBufferPool::CompressFreeBuffer(int size)
{
	XJMutexLocalLock locallock(m_csFreeBuffer);

	unsigned long now = TimeGetTime();

	while(m_lsFreeBuffer.Size() > size)
	{
		TBuffer* pBuffer = m_lsFreeBuffer.Front();

		if(now - pBuffer->freeTime >= m_dwBufferLockTime)
		{
			m_lsFreeBuffer.PopFront();
			TBuffer::Destruct(pBuffer);
		}
		else
			break;
	}
}

void XJBufferPool::PutCacheBuffer(unsigned long dwID)
{
	ASSERT(dwID != 0);

	XJRWWriteLock locallock(m_csBufferMap);
	m_mpBuffer[dwID] = PickFreeBuffer(dwID);
}

TBuffer* XJBufferPool::PickFreeBuffer(unsigned long dwID)
{
	ASSERT( dwID != 0);

	TBuffer* pBuffer = NULL;

	if(m_lsFreeBuffer.Size() > 0)
	{
		XJMutexLocalLock locallock(m_csFreeBuffer);

		if(m_lsFreeBuffer.Size() > 0)
		{
			pBuffer = m_lsFreeBuffer.Front();

			if(GetTimeGap32(pBuffer->freeTime) >= m_dwBufferLockTime)
				m_lsFreeBuffer.PopFront();
			else
				pBuffer = NULL;
		}
	}

	if(pBuffer)	pBuffer->id	= dwID;
	else		pBuffer		= TBuffer::Construct(*this, dwID);

	ASSERT(pBuffer);
	return pBuffer;
}

TBuffer* XJBufferPool::FindCacheBuffer(unsigned long dwID)
{
	ASSERT(dwID != 0);

	TBuffer* pBuffer = NULL;

	XJRWReadLock locallock(m_csBufferMap);

	TBufferPtrMapCI it = m_mpBuffer.find(dwID);
	if(it != m_mpBuffer.end())
		pBuffer = it->second;

	return pBuffer;
}

void XJBufferPool::Clear()
{
	{
		XJRWWriteLock locallock(m_csBufferMap);

		for(TBufferPtrMapCI it = m_mpBuffer.begin(); it != m_mpBuffer.end(); ++it)
			TBuffer::Destruct(it->second);

		m_mpBuffer.clear();
	}

	{
		XJMutexLocalLock locallock(m_csFreeBuffer);
		m_lsFreeBuffer.Clear();
	}

	m_itPool.Clear();
	m_heap.Reset();
}


}//namespace XJUtilClass
